﻿using Microsoft.EntityFrameworkCore.Metadata.Internal;
using StackExchange.Redis;

namespace 订单处理系统.IService.impl
{
    public interface IRedisService
    {
        Task<string?> GetStringAsync(string key);

        Task SetStringAsync(string key, string value, TimeSpan? expire = null);

        Task SetIntAsync(string key, int value, TimeSpan? expire = null);

        Task<int> GetIntAsync(string key);

        Task<long> DecrementAsync(string key);

        Task IncrementAsync(string key);

        Task<bool> keyExitstsAsync(string key);

        Task<bool> DeleteKeyAsync(string key);

        Task<bool> AcquireLockAsync(string key, string value, TimeSpan expirey);

        Task<bool> ReleaseLockAsync(string key, string value);
    }

    public class RedisService : IRedisService
    {
        private readonly IDatabase _db;

        public RedisService(IConnectionMultiplexer connectionMultiplexer)
        {
            _db = connectionMultiplexer.GetDatabase();
        }

        public async Task<bool> AcquireLockAsync(string key, string value, TimeSpan expirey) => await _db.StringSetAsync(key, value, expirey, When.NotExists);

        public Task<long> DecrementAsync(string key) => _db.StringDecrementAsync(key);

        public async Task<bool> DeleteKeyAsync(string key) => await _db.KeyDeleteAsync(key);

        public async Task<int> GetIntAsync(string key)
        {
            var val = await _db.StringGetAsync(key);
            return int.TryParse(val, out var result) ? result : 0;
        }

        public async Task<string?> GetStringAsync(string key) => await _db.StringGetAsync(key);

        public async Task IncrementAsync(string key)
        {
            await _db.StringIncrementAsync(key);
        }

        public async Task<bool> keyExitstsAsync(string key) => await _db.KeyExistsAsync(key);

        public async Task<bool> ReleaseLockAsync(string key, string value)
        {
            var script = @"
            if redis.call('get', KEYS[1]) == ARGV[1]
            then
                return redis.call('del', KEYS[1])
            else
                return 0
            end";
            var result = await _db.ScriptEvaluateAsync(script, new RedisKey[] { key }, new RedisValue[] { value });
            return (int)result == 1;
        }

        public Task SetIntAsync(string key, int value, TimeSpan? expire = null) => _db.StringSetAsync(key, value, expire);

        public async Task SetStringAsync(string key, string value, TimeSpan? expire = null) => await _db.StringSetAsync(key, value, expire);
    }
}